%{
package compiler;
import java.io.IOException;
import java.io.PushbackReader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import compiler.Exceptions.OutOfLimitsException;
import compiler.Exceptions.SyntaxErrorException;
import java.util.NoSuchElementException;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
%}

/* declaraciones YACC */
%token OPSUMA OPRESTA OPMULT OPDIV CONCAT OPASIGN MAYOR MENOR MAYORIGUAL MENORIGUAL IGUALIGUAL DISTINTO ID CTESTRING CTE PYC DOSPUNTOS COMA INIPAREN FINPAREN INICOR FINCOR GUIONBAJO INILLAVE FINLLAVE FOR ROF IF THEN DO ELSE FI VAR ENDVAR INT FLOAT DOUBLE LONG STRING NOT AND OR FILTERC WPRINT

%right OPASIGN
%left OPRESTA OPSUMA
%left OPMULT OPDIV

/* reglas */
%%
programa: declaraciones sentencias
{ 
	dbug("declaraciones sentencias " + $$);
	programaPtr = Arbol.crearNodo("programa", sentenciasStack.pop(), null);
	programaStack.push(programaPtr);
	mostrarArbol();
	Assembler assembler = generaAssembler(programaPtr);
	writeToFile(assembler.getAssembler());
}
 | outputs
{ 
	dbug("output" + $$);
	programaPtr = outputsStack.pop();
	programaStack.push(programaPtr);
	mostrarArbol();
	Assembler assembler = generaAssembler(programaPtr);
	writeToFile(assembler.getAssembler());
}
 | declaraciones
{
	dbug("declaraciones " + $$);
}
 ;

outputs: outputs output
{
	dbug("outputs output " + $$);
	outputsPtr = Arbol.crearNodo("outputs", outputsStack.pop(), outputStack.pop());
	outputsStack.push(outputsPtr);
}
 | output
 {
 	dbug("output " + $$);
 	outputsPtr = outputStack.pop();
 	outputsStack.push(outputsPtr);
 }
 ;

sentencias: sentencias sentencia 
{ 
	dbug("sentencias sentencia " + $$);
	sentenciasPtr = Arbol.crearNodo("sentencias", sentenciasStack.pop(), sentenciaStack.pop());
	sentenciasStack.push(sentenciasPtr);
}
 | sentencia 
{ 
	dbug("sentencia " + $$);
	sentenciasPtr = sentenciaStack.pop();
	sentenciasStack.push(sentenciasPtr);
}
 ;

sentencia: iteracion
{ 
	dbug("iteracion" + $$);
	sentenciaPtr = iteracionStack.pop();
	sentenciaStack.push(sentenciaPtr);
}
 | decision
{ 
	dbug("decision" + $$);
	sentenciaPtr = decisionStack.pop();
	sentenciaStack.push(sentenciaPtr);
}
 | asignacion PYC
{ 
	dbug("asignacion PYC" + $$);
	sentenciaPtr = asignacionStack.pop();
	sentenciaStack.push(sentenciaPtr);
}
 | expresion
{ 
	dbug("expresion" + $$);
	sentenciaPtr = expresionStack.pop();
	sentenciaStack.push(sentenciaPtr);
}
 | output
{ 
	dbug("output" + $$);
	sentenciaPtr = outputStack.pop();
	sentenciaStack.push(sentenciaPtr);
}
 ;

 iteracion: iteracionh DO sentencias ROF 
{ 
 	dbug("FOR INIPAREN asignacion PYC condicion PYC asignacion FINPAREN DO sentencias ROF " + $$);
 	iteracionPtr = Arbol.crearNodo("FOR", iteracionhStack.pop(), sentenciasStack.pop());
	iteracionStack.push(iteracionPtr);
}
 ;

iteracionh: FOR INIPAREN asignacion PYC condicion PYC asignacion FINPAREN
{ 
 	dbug("FOR INIPAREN asignacion PYC condicion PYC asignacion FINPAREN" + $$);
 	Nodo primero = asignacionStack.pop();
	Nodo segundo = asignacionStack.pop();
	iteracionhPtr = Arbol.crearNodo("parte1", segundo, condicionStack.pop());
	iteracionhStack.push(iteracionhPtr);

	iteracionhPtr = Arbol.crearNodo("parte2", iteracionhStack.pop(), primero);
	iteracionhStack.push(iteracionhPtr);
}
 ;

decision: IF INIPAREN condicion FINPAREN THEN sentencias ELSE sentencias FI 
{ 
	dbug("IF INIPAREN condicion FINPAREN THEN sentencias ELSE sentencias FI " + $$);
	Nodo primero = sentenciasStack.pop();
	Nodo segundo = sentenciasStack.pop();
	decisionPtr = Arbol.crearNodo("resultado", segundo, primero);
	decisionStack.push(decisionPtr);

	decisionPtr = Arbol.crearNodo("condicion", condicionStack.pop(), decisionStack.pop());
	decisionStack.push(decisionPtr);

}
 | IF INIPAREN condicion FINPAREN THEN sentencias FI 
{ 
	dbug("IF INIPAREN condicion FINPAREN THEN sentencias FI " + $$);
	decisionPtr = Arbol.crearNodo("resultado", sentenciasStack.pop(), null);
	decisionStack.push(decisionPtr);

	decisionPtr = Arbol.crearNodo("condicion", condicionStack.pop(), decisionStack.pop());
	decisionStack.push(decisionPtr);
}
 ;

filtrar: FILTERC INIPAREN condicionfilterc COMA INICOR operaciones FINCOR FINPAREN 
{ 
	dbug("FILTERC INIPAREN condicionfilterc COMA INICOR operaciones FINCOR FINPAREN " + $$);

	Nodo aux = condicionfiltercStack.pop();
	Nodo auxCond;
	String varFilter = pedirVar();

	filtrarPtr = Arbol.crearNodo("OPASIGN", Arbol.crearHoja(varFilter), Arbol.crearHoja("__0__"));
	filtrarStack.push(filtrarPtr);

	while(!operacionesStack.isEmpty()) {
		auxCond = aux.copiar();
		if(auxCond.izquierdo.elemento.toString() != "GUIONBAJO") { 
			if(auxCond.elemento.toString() == "NOT") {
				auxCond.izquierdo.izquierdo = operacionesStack.pop();
			}
			else {/* if ANDOR*/
				auxCond.izquierdo.izquierdo = operacionesStack.pop();
				auxCond.derecho.izquierdo = auxCond.izquierdo.izquierdo;
			}
		}
		else
			auxCond.izquierdo = operacionesStack.pop();

		Nodo iMasMas = Arbol.crearNodo("OPASIGN", Arbol.crearHoja(varFilter), Arbol.crearNodo("OPSUMA",Arbol.crearHoja(varFilter), Arbol.crearHoja("__1__")));
		Nodo auxPtr = Arbol.crearNodo("resultado", iMasMas, null);
		Nodo ifNodo = Arbol.crearNodo("condicion", auxCond, auxPtr);
		filtrarPtr = Arbol.crearNodo("sentencias", filtrarStack.isEmpty() ? null : filtrarStack.pop(), ifNodo);
		filtrarStack.push(filtrarPtr);
	}

	filtrarPtr = Arbol.crearNodo("sentencias",filtrarPtr,Arbol.crearHoja(varFilter));
	filtrarStack.push(filtrarPtr);
}
 ;

operaciones: operaciones COMA express
{ 
	dbug("operaciones COMA express" + $$);
	operacionesPtr = expressStack.pop();
	operacionesStack.push(operacionesPtr);
}
 | express
{ 
	dbug("express" + $$);
	operacionesPtr = expressStack.pop();
	operacionesStack.push(operacionesPtr);
}
 ;

condicionfilterc: GUIONBAJO comparador operando // THEN ID = ID + 1
{ 
	dbug("GUIONBAJO comparador operando" + $$);
	condicionfiltercPtr = Arbol.crearNodo(comparadorStack.pop().elemento.toString(), Arbol.crearHoja("GUIONBAJO"), operandoStack.pop());
	condicionfiltercStack.push(condicionfiltercPtr);
}
 | NOT condicionfilterc
{ 
	dbug("NOT GUIONBAJO comparador operando" + $$);
	condicionfiltercPtr = Arbol.crearNodo("NOT", condicionfiltercStack.pop(), null);
	condicionfiltercStack.push(condicionfiltercPtr);
}
 | GUIONBAJO comparador operando andor GUIONBAJO comparador operando
{ 
	dbug("GUIONBAJO comparador operando andor GUIONBAJO comparador operando" + $$);
	condicionfiltercPtr = Arbol.crearNodo(comparadorStack.pop().elemento.toString(), Arbol.crearHoja("GUIONBAJO"), operandoStack.pop());
	condicionfiltercStack.push(condicionfiltercPtr);

	condicionfiltercPtr = Arbol.crearNodo(comparadorStack.pop().elemento.toString(), Arbol.crearHoja("GUIONBAJO"), operandoStack.pop());
	condicionfiltercStack.push(condicionfiltercPtr);

	condicionfiltercPtr = Arbol.crearNodo(andorStack.pop().elemento.toString(), condicionfiltercStack.pop(), condicionfiltercStack.pop());
	condicionfiltercStack.push(condicionfiltercPtr);
}
 ;

asignacion: ID OPASIGN asignacion 
{ 
	dbug("ID OPASIGN asignacion " + $$);
	asignacionPtr = Arbol.crearNodo("OPASIGN", Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true)), asignacionStack.pop());
	asignacionStack.push(asignacionPtr);

}
 | expresion
{ 
	dbug("expresion de asignacion" + $$);
	asignacionPtr = expresionStack.pop();
	asignacionStack.push(asignacionPtr);
}
 | concatenacion
{ 
	dbug("expresion de concatenacion" + $$);
	asignacionPtr = concatenacionStack.pop();
	asignacionStack.push(asignacionPtr);
}

 ;

asignomas: INILLAVE ID OPASIGN express FINLLAVE 
{ 
	$$ = $2;
	dbug("INILLAVE ID OPASIGN express FINLLAVE " + $$);
	asignomasPtr = Arbol.crearNodo("asignomas", Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true)), expressStack.pop());
	asignomasStack.push(asignomasPtr);
}
 ;

declaracion: INICOR variables FINCOR DOSPUNTOS INICOR tiposdedatos FINCOR 
{ 
	dbug("INICOR variables FINCOR DOSPUNTOS INICOR tiposdedatos FINCOR " + $$);
	inicializarVariables(stackVariables, stackTipos);
};

variables: ID COMA variables 
{ 
	verificarRepetidos();
	$$=intVar++; 
	dbug("ID COMA variables " + $$); 
	stackVariables.push($$);
}
 | ID 
{ 
	verificarRepetidos();
	$$=intVar++; 
	dbug("ID " + $$);
	stackVariables.push($$);
}
 ;

declaraciones: VAR declarecur ENDVAR
{ 
	dbug("VAR declarecur ENDVAR" + $$);
}
 ;

declarecur: declarecur declaracion
{ 
	dbug("declarecur declaracion" + $$);
}
 | declaracion
{ 
	dbug("declaracion" + $$);
}
 ;

tiposdedatos: tipo COMA tiposdedatos
{ 
	dbug("tipo COMA tiposdedatos" + $$);
}
 | tipo 
{ 
	dbug("tipo " + $$);
}
 ;

constante: CTE 
{ 
	dbug("CTE " + $$);
	constantePtr = Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true));
	constanteStack.push(constantePtr);
}
 | CTESTRING 
{ 
	dbug("CTESTRING " + $$);
	constantePtr = Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true));
	constanteStack.push(constantePtr);
}
 ;

concatenacion: fact CONCAT fact 
{ 
	dbug("fact CONCAT fact " + $$);
	concatenacionPtr = Arbol.crearNodo("CONCAT", factStack.pop(), factStack.pop());
	concatenacionStack.push(concatenacionPtr);
}
 ;

output: WPRINT INIPAREN fact FINPAREN PYC 
{ 
	dbug("WPRINT INIPAREN fact FINPAREN PYC " + $$);
	outputPtr = Arbol.crearNodo("WPRINT", factStack.pop(), null);
	outputStack.push(outputPtr);
}
 ;

condicion: condsimple
{ 
	dbug("condsimple" + $$);
	condicionPtr = condsimpleStack.pop();
	condicionStack.push(condicionPtr);
}
 | condsimple andor condsimple
{ 
	dbug("condsimple andor condsimple" + $$);
	Nodo primero = condsimpleStack.pop();
	Nodo segundo = condsimpleStack.pop();
	condicionPtr = Arbol.crearNodo(andorStack.pop().elemento.toString(), segundo, primero);
	condicionStack.push(condicionPtr);
}
 | NOT condsimple
{ 
	dbug("NOT expresion comparador expresion" + $$);
	condicionPtr = Arbol.crearNodo("NOT", condsimpleStack.pop(), null);
	condicionStack.push(condicionPtr);
}
 ;

condsimple: expresion comparador expresion
{ 
	dbug("expresion comparador expresion" + $$);
	Nodo primero = expresionStack.pop();
	Nodo segundo = expresionStack.pop();
	condsimplePtr = Arbol.crearNodo(comparadorStack.pop().elemento.toString(), segundo, primero);
	condsimpleStack.push(condsimplePtr);
}
 ;

express : express OPSUMA term
{ 
	dbug("express OPSUMA term" + $$);
	expressPtr = Arbol.crearNodo("OPSUMA", expressStack.pop(), termStack.pop());
	expressStack.push(expressPtr);
}
 | term
{ 
	dbug("term" + $$);
	expressPtr = termStack.pop();
	expressStack.push(expressPtr);
}
 | express OPRESTA term
{ 
	dbug("express OPRESTA term" + $$);
	expressPtr = Arbol.crearNodo("OPRESTA", expressStack.pop(), termStack.pop());
	expressStack.push(expressPtr);
}
 ;

term : term OPMULT fact
{ 
	dbug("term OPMULT fact" + $$);
	termPtr = Arbol.crearNodo("OPMULT", termStack.pop(), factStack.pop());
	termStack.push(termPtr);
}
 | fact
{ 
	dbug("fact" + $$);
	termPtr = factStack.pop();
	termStack.push(termPtr);
}
 | term OPDIV fact
{ 
	dbug("term OPDIV fact" + $$);
	termPtr = Arbol.crearNodo("OPDIV", termStack.pop(), factStack.pop());
	termStack.push(termPtr);
}
 ;

fact : ID 
{ 
	dbug("ID " + $$);
	factPtr = Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true));
	factStack.push(factPtr);
}
 | constante 
{ 
	dbug("constante " + $$);
	factPtr = constanteStack.pop();
	factStack.push(factPtr);
}
 ;

expresion: expresion OPSUMA termino
{ 
	dbug("expresion OPSUMA termino" + $$);
	expresionPtr = Arbol.crearNodo("OPSUMA", expresionStack.pop(), terminoStack.pop());
	expresionStack.push(expresionPtr);
}
 | termino
{ 
	dbug("termino" + $$);
	expresionPtr = terminoStack.pop();
	expresionStack.push(expresionPtr);
}
 | expresion OPRESTA termino
{ 
	dbug("expresion OPRESTA termino" + $$);
	expresionPtr = Arbol.crearNodo("OPRESTA", expresionStack.pop(), terminoStack.pop());
	expresionStack.push(expresionPtr);
}
 ;

termino: termino OPMULT factor
{ 
	dbug("termino OPMULT factor" + $$);
	terminoPtr = Arbol.crearNodo("OPMULT", terminoStack.pop(), factorStack.pop());
	terminoStack.push(terminoPtr);
}
 | factor
{ 
	dbug("factor" + $$);
	terminoPtr = factorStack.pop();
	terminoStack.push(terminoPtr);
}
 | termino OPDIV factor
{ 
	dbug("termino OPDIV factor" + $$);
	terminoPtr = Arbol.crearNodo("OPDIV", terminoStack.pop(), factorStack.pop());
	terminoStack.push(terminoPtr);
}
 ;

factor: ID 
{ 
	dbug("ID " + $$);
	factorPtr = Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true));
	factorStack.push(factorPtr);
}
 | constante 
{ 
	dbug("constante " + $$);
	factorPtr = constanteStack.pop();
	factorStack.push(factorPtr);
}
 | asignomas
{ 
	dbug("asignomas" + $$);
	factorPtr = asignomasStack.pop();
	factorStack.push(factorPtr);
}
 | filtrar
{ 
	dbug("filtrar" + $$);
	factorPtr = filtrarStack.pop();
	factorStack.push(factorPtr);
}
 | INIPAREN expresion FINPAREN
{
	dbug("INIPAREN expresion FINPAREN " + $$);
	factorPtr = expresionStack.pop();
	factorStack.push(factorPtr);
}
 ;

operando: ID 
{ 
	dbug("ID " + $$);
	operandoPtr = Arbol.crearHoja(Metodos.getTabla_simbolos().getElemento($$, true));
	operandoStack.push(operandoPtr);
}
 | constante 
{ 
	dbug("constante " + $$);
	operandoPtr = constantePtr;
	operandoStack.push(operandoPtr);
}
 ;

tipo: INT	
{ 
	dbug("INT	" + $$);
	stackTipos.push("INT");
}
 | FLOAT	
{ 
	dbug("FLOAT	" + $$);
	stackTipos.push("FLOAT");
}
 | DOUBLE	
{ 
	dbug("DOUBLE	" + $$);
	stackTipos.push("DOUBLE");
}
 | LONG		
{ 
	dbug("LONG		" + $$);
	stackTipos.push("LONG");
}
 | STRING	
{ 
	dbug("STRING	" + $$);
	stackTipos.push("STRING");
}
 ;

comparador: MAYOR 
{ 
	dbug("MAYOR " + $$);
	comparadorPtr = Arbol.crearHoja("MAYOR");
	comparadorStack.push(comparadorPtr);
}
 | MENOR 
{ 
	dbug("MENOR " + $$);
	comparadorPtr = Arbol.crearHoja("MENOR");
	comparadorStack.push(comparadorPtr);
}
 | MAYORIGUAL 
{ 
	dbug("MAYORIGUAL " + $$);
	comparadorPtr = Arbol.crearHoja("MAYORIGUAL");
	comparadorStack.push(comparadorPtr);
}
 | MENORIGUAL 
{ 
	dbug("MENORIGUAL " + $$);
	comparadorPtr = Arbol.crearHoja("MENORIGUAL");
	comparadorStack.push(comparadorPtr);
}
 | IGUALIGUAL 
{ 
	dbug("IGUALIGUAL " + $$);
	comparadorPtr = Arbol.crearHoja("IGUALIGUAL");
	comparadorStack.push(comparadorPtr);
}
 | DISTINTO 
{ 
	dbug("DISTINTO " + $$);
	comparadorPtr = Arbol.crearHoja("DISTINTO");
	comparadorStack.push(comparadorPtr);
}
 ;

andor: AND 
{ 
	dbug("AND " + $$);
	andorPtr = Arbol.crearHoja("AND");
	andorStack.push(andorPtr); 
}
 | OR 
{ 
	dbug("OR " + $$);
	andorPtr = Arbol.crearHoja("OR");
	andorStack.push(andorPtr);
}
 ;
%%

/* codigo */

static PushbackReader reader = null;
static int[][] nuevo_estado = null;
static String[][] proceso = null;
static Integer linea = 1;
private Deque<Integer> stackVariables = new ArrayDeque<Integer>();
private Deque<String> stackTipos = new ArrayDeque<String>();
private Deque<String> stackAux = new ArrayDeque<String>();
private Deque<String> stackVar = new ArrayDeque<String>();
static int intTipo = 0;
static int intVar = 0;
static int indexSalto = 0;
static int indexAritmetico = 1;
static int indexFor = 0;
static int indexVar = 1;
static boolean debuggear = false;
static boolean nuestroError = false;

/* pilas para el arbol sintactico */
private Deque<Nodo> programaStack = new ArrayDeque<Nodo>();
private Deque<Nodo> sentenciasStack = new ArrayDeque<Nodo>();
private Deque<Nodo> sentenciaStack = new ArrayDeque<Nodo>();
private Deque<Nodo> iteracionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> iteracionhStack = new ArrayDeque<Nodo>();
private Deque<Nodo> decisionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> filtrarStack = new ArrayDeque<Nodo>();
private Deque<Nodo> operacionesStack = new ArrayDeque<Nodo>();
private Deque<Nodo> condicionfiltercStack = new ArrayDeque<Nodo>();
private Deque<Nodo> asignacionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> asignomasStack = new ArrayDeque<Nodo>();
private Deque<Nodo> constanteStack = new ArrayDeque<Nodo>();
private Deque<Nodo> concatenacionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> outputStack = new ArrayDeque<Nodo>();
private Deque<Nodo> outputsStack = new ArrayDeque<Nodo>();
private Deque<Nodo> condicionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> condsimpleStack = new ArrayDeque<Nodo>();
private Deque<Nodo> expressStack = new ArrayDeque<Nodo>();
private Deque<Nodo> termStack = new ArrayDeque<Nodo>();
private Deque<Nodo> factStack = new ArrayDeque<Nodo>();
private Deque<Nodo> expresionStack = new ArrayDeque<Nodo>();
private Deque<Nodo> terminoStack = new ArrayDeque<Nodo>();
private Deque<Nodo> factorStack = new ArrayDeque<Nodo>();
private Deque<Nodo> operandoStack = new ArrayDeque<Nodo>();
private Deque<Nodo> comparadorStack = new ArrayDeque<Nodo>();
private Deque<Nodo> andorStack = new ArrayDeque<Nodo>();
/* fin elementos arbol sintactico */

/* elementos del arbol sintactico */
private Nodo programaPtr = new Nodo();
private Nodo sentenciasPtr = new Nodo();
private Nodo sentenciaPtr = new Nodo();
private Nodo iteracionPtr = new Nodo();
private Nodo iteracionhPtr = new Nodo();
private Nodo decisionPtr = new Nodo();
private Nodo filtrarPtr = new Nodo();
private Nodo operacionesPtr = new Nodo();
private Nodo condicionfiltercPtr = new Nodo();
private Nodo asignacionPtr = new Nodo();
private Nodo asignomasPtr = new Nodo();
private Nodo constantePtr = new Nodo();
private Nodo concatenacionPtr = new Nodo();
private Nodo outputPtr = new Nodo();
private Nodo outputsPtr = new Nodo();
private Nodo condicionPtr = new Nodo();
private Nodo condsimplePtr = new Nodo();
private Nodo expressPtr = new Nodo();
private Nodo termPtr = new Nodo();
private Nodo factPtr = new Nodo();
private Nodo expresionPtr = new Nodo();
private Nodo terminoPtr = new Nodo();
private Nodo factorPtr = new Nodo();
private Nodo operandoPtr = new Nodo();
private Nodo comparadorPtr = new Nodo();
private Nodo andorPtr = new Nodo();
/* fin elementos arbol sintactico */

/* yylex devuelve -1 si ocurre un error, 0 si es fin de archivo o un token int */
int yylex()
{
	int tok = -1;
	int estado = 0;
	int estado_final = 27;
	int columna = -1;
	Integer caracter = 0;

	Metodos m = new Metodos();

	try
	{
		while(estado != estado_final)
		{
			caracter = reader.read();
			char c = (char) caracter.intValue(); 

			if(c == '\n')
				linea ++;

			if(caracter == -1)
			{ // fin de archivo
				if(estado == 0)
					return 0;

				if(estado == 4)
					columna = getEvento(caracter);
				
				int futuro = nuevo_estado[estado][columna];
				
				if(futuro != estado_final && estado != 4) 
				{
					return -1;
				}

				else
				{ 	// Es el fin de archivo, el estado que viene es el estado final y el estado actual no es 0
					// Es el caso de la finalizacion de archivo correcta, cuando hay que cerrar el ultimo token
					Object ret = Metodos.class.getMethod(proceso[estado][columna], Integer.class).invoke(m, caracter.intValue());
					if(ret != null && ret instanceof Integer)
						tok = ((Integer) ret).intValue();
					if(m.esToken())
					{
						yylval = m.getYylval();
					}
					
					return tok;
				}
			}

			columna = getEvento(caracter);
			Object ret = Metodos.class.getMethod(proceso[estado][columna], Integer.class).invoke(m, caracter.intValue());
			if(ret != null && ret instanceof Integer)
				tok = ((Integer) ret).intValue();

			estado = nuevo_estado[estado][columna];
		}

		if(hayQueRetornar(tok))
		{
			reader.unread(caracter.intValue());
		}

		if (m.esToken())
			yylval = m.getYylval();
	} 

	catch (IOException e)
	{
		yyerror(e.getMessage());
		return -1;
	}

	catch (IllegalAccessException e)
	{
		yyerror(e.getMessage());
		return -1;
	}

	catch (IllegalArgumentException e)
	{
		yyerror(e.getMessage());
		return -1;
	}

	catch (InvocationTargetException e)
	{
		nuestroError = true;
		if(e.getTargetException() instanceof SyntaxErrorException)
		{
			SyntaxErrorException see = (SyntaxErrorException) e.getTargetException();
			yyerror(see.getMessage().replace("LINEA", linea.toString()));
			return -1;
		}
		if(e.getTargetException() instanceof OutOfLimitsException){
			OutOfLimitsException o = (OutOfLimitsException) e.getTargetException();
			yyerror(o.getMessage());
			return -1;
		}
			
		yyerror(e.getMessage());
		return -1;
	}

	catch (NoSuchMethodException e)
	{
		yyerror(e.getMessage());
		return -1;
	}

	catch (SecurityException e)
	{
		yyerror(e.getMessage());
		return -1;
	}

	catch (SyntaxErrorException e)
	{
		yyerror(e.getMessage().replace("LINEA", linea.toString()));
		return -1;
	} 

	return tok;
}

private static boolean hayQueRetornar(int tok)
{
	switch(tok)
	{
		case CTESTRING:
			return false;
		case CONCAT:
			return false;
		case IGUALIGUAL:
			return false;
		case DISTINTO:
			return false;
		default:
			return true;
	}
}

private static int getEvento(int caracter) throws SyntaxErrorException
{
	String aux = new String();
	aux += (char) caracter;
	if(caracter == -1)
		return 24;
	
	
	char c = (char) caracter;
	if(c >= '0' && c <= '9')
		return 1;
	if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
		return 2;
	if("¡@#$·%&|¿?^".contains(aux)) {
		return 23;
	}
	switch(c) {
	case '"':
		return 0;
	case '=':
		return 3;
	case '+':
		return 4;
	case '*':
		return 5;
	case '/':
		return 6;
	case '>':
		return 7;
	case '<':
		return 8;
	case '_':
		return 9;
	case '(':
		return 10;
	case ')':
		return 11;
	case '[':
		return 12;
	case ']':
		return 13;
	case',':
		return 14;
	case ':':
		return 15;
	case ';':
		return 16;
	case '-':
		return 17;
	case '{':
		return 18;
	case '}':
		return 19;
	case '!':
		return 20;
	case '.':
		return 21;
	case ' ':
		return 22;
	case '\n':
		return 22;
	case '\t':
		return 22;
	case '\r':
		return 22;
	}

	// Caracter no manejado por nuestro lenguaje
	System.out.println("Error: " + (char)caracter);
	throw new SyntaxErrorException((char) caracter);
}

/* usada por yacc para el manejo de errores */
static void yyerror(String s)
{
	if(!nuestroError) {
		System.out.println("Error de sintaxis (yacc) en linea " + (linea == null ? "desconocida" : linea));
	}
	else {
		System.out.println("(MMW) ->>");
		System.out.println("\t"+s);
	}
	System.exit(1);
}

public static String stringanizer(short i)
{
	switch(i)
	{
		case ID:
			return "<ID>";
		case CTESTRING:
			return "<CTESTRING>";
		case CTE:
			return "<CTE>";
		case FOR:
			return "<FOR>";
		case ROF:
			return "<ROF>";
		case IF:
			return "<IF>";
		case THEN:
			return "<THEN>";
		case ELSE:
			return "<ELSE>";
		case FI:
			return "<FI>";
		case VAR:
			return "<VAR>";
		case ENDVAR:
			return "<ENDVAR>";
		case INT:
			return "<INT>";
		case FLOAT:
			return "<FLOAT>";
		case DOUBLE:
			return "<DOUBLE>";
		case LONG:
			return "<LONG>";
		case STRING:
			return "<STRING>";
		case NOT:
			return "<NOT>";
		case OPSUMA:
			return "<OPSUMA>";
		case OPRESTA:
			return "<OPRESTA>";
		case OPMULT:
			return "<OPMULT>";
		case OPDIV:
			return "<OPDIV>";
		case AND:
			return "<AND>";
		case OR:
			return "<OR>";
		case FILTERC:
			return "<FILTERC>";
		case PYC:
			return "<PYC>";
		case OPASIGN:
			return "<OPASIGN>";
		case DOSPUNTOS:
			return "<DOSPUNTOS>";
		case COMA:
			return "<COMA>";
		case MAYOR:
			return "<MAYOR>";
		case MENOR:
			return "<MENOR>";
		case MAYORIGUAL:
			return "<MAYORIGUAL>";
		case MENORIGUAL:
			return "<MENORIGUAL>";
		case IGUALIGUAL:
			return "<IGUALIGUAL>";
		case DISTINTO:
			return "<DISTINTO>";
		case INIPAREN:
			return "<INIPAREN>";
		case FINPAREN:
			return "<FINPAREN>";
		case CONCAT:
			return "<CONCAT>";
		case INICOR:
			return "<INICOR>";
		case FINCOR:
			return "<FINCOR>";
		case GUIONBAJO:
			return "<GUIONBAJO>";
		case INILLAVE:
			return "<INILLAVE>";
		case DO:
			return "<DO>";
		case WPRINT:
			return "<WPRINT>";
		case FINLLAVE:
			return "<FINLLAVE>";
	}
	
	return "Token no reconocido";
}

public static void inicializarVariables(Deque<Integer> stackVariables, Deque<String> stackTipos)
{
	Integer variable = null;
	String tipo = null;
	Integer diff = stackTipos.size() - stackVariables.size();

 	while(diff > 0) {
	 	tipo = stackTipos.pop();
	 	diff --;
	 }

 	while(diff < 0) {
	 	variable = stackVariables.pop();
	 	diff ++;
	 }

	while(!stackVariables.isEmpty())
	{
		variable = stackVariables.poll();
		try
		{
			tipo = stackTipos.pop();
		}
		
		catch (NoSuchElementException e){
			// Eliminar el resto de las variables
			stackVariables.clear();
			stackTipos.clear();
			return;
		}

		Metodos.getTabla_simbolos().modificarTipo(variable, tipo);
	}

	stackVariables.clear();
	stackTipos.clear();
}

public void mostrarArbol() {
	try {
		programaPtr.crearXML("tree.xml");
	} catch (IOException e) {
		e.printStackTrace();
	} 
	programaPtr.dibujar();
}

public static void verificarRepetidos()
{
	String aux;

	for(int i=0 ; i<Metodos.ListaVariables.size() ; i++)
	{
		aux = Metodos.ListaVariables.get(i);
		if(Metodos.ListaVariables.lastIndexOf(aux) != Metodos.ListaVariables.indexOf(aux))
			yyerror("Declaracion de variable \"" + aux + "\" duplicada");
	}			
}

public Operaciones enumerator(String s) {
	Operaciones[] op = Operaciones.values();
	for(Operaciones o : op) {
		if(o.toString().equalsIgnoreCase(s))
			return o;
	}
	return null;
}

@SuppressWarnings("incomplete-switch")
public Assembler generaAssembler(Nodo raiz) {
	String padre = raiz.elemento.toString();
	Operaciones operacion = enumerator(padre);
	Assembler a = new Assembler();

	// Estas operaciones resuelven por su cuenta todo el subarbol izquierdo,
	// el derecho, su propio assembler y lo devuelven
	if(operacion != null)
		switch(operacion) {
			case CONCAT:{
				Assembler aux1 = generaAssembler(raiz.izquierdo);
				Assembler aux2 = generaAssembler(raiz.derecho);

				String tipo1 = aux1.getTipo();
				String tipo2 = aux2.getTipo();

				// Si no son cadenas no puedo concatenar
				if(!(tipo1.equalsIgnoreCase("cteString") || tipo1.equalsIgnoreCase("string")) || !(tipo2.equalsIgnoreCase("cteString") || tipo2.equalsIgnoreCase("string"))) {
					System.out.println("No se pueden concatenar " + tipo2 + " a " + tipo1);
					System.exit(1);
				}

				String nombre1 = aux1.getResultado();
				String nombre2 = aux2.getResultado();

				// Copio la segunda cadena (la que es anexada) a la variable reservada
				a.concatAssembler(prettyPrint(null , "MOV", "si,OFFSET "+ nombre2.toString(),"debe apuntar al origen de cad1"));
				a.concatAssembler(prettyPrint(null , "MOV", "di,OFFSET __concat__",null));
				a.concatAssembler(prettyPrint(null , "CALL COPIAR", "", null));

				// Realizo la concatenacion, primero va la cadena 2 porque es lo que va a continuacion de la cadena 1
				a.concatAssembler(prettyPrint(null , "MOV", "si,OFFSET "+ nombre1.toString(), null));
				a.concatAssembler(prettyPrint(null , "MOV","di,OFFSET __concat__", null));
				a.concatAssembler(prettyPrint(null , "CALL CONCAT", "", null));
				a.setResultado("__concat__");
				a.setTipo("cteString");
				
				// Hasta acá concatené en un auxiliar
				return a;
			}

			case OPASIGN: {
				Assembler aux = generaAssembler(raiz.derecho);
				a.setAssembler(aux.getAssembler());
				String resultado = aux.getResultado();
				String tipo1 = Metodos.getTabla_simbolos().getTipo(raiz.izquierdo.elemento.toString());

				if(tipo1 == null) { // es __var__ únicamente , __concat__ no se usa del lado izquierdo, ni __1__
					tipo1 = "INT";
				}

				String tipo2 = aux.getTipo();
				if(tipo1.equalsIgnoreCase("cteString") || tipo1.equalsIgnoreCase("string")) {
					if(!(tipo2.equalsIgnoreCase("cteString") || tipo2.equalsIgnoreCase("string"))) {
						System.out.println("No se puede asignar " + tipo2 + " a " + tipo1);
						System.exit(1);
					}
				}
				else {
					if(!tipo1.endsWith(tipo2.toUpperCase()) && !tipo2.endsWith(tipo1.toUpperCase())){
							System.out.println("No se puede asignar " + tipo2 + " a " + tipo1);
							System.exit(1);
					}
				}

				if (raiz.derecho.elemento.toString().equals("CONCAT")) {
					a.concatAssembler(prettyPrint(null, "MOV","si, OFFSET __concat__", null));
					a.concatAssembler(prettyPrint(null, "MOV","di, OFFSET "+ raiz.izquierdo.elemento.toString(), null));
					a.concatAssembler(prettyPrint(null, "CALL COPIAR", null, null));
				} 
				else
					if(Metodos.getTabla_simbolos().contiene(resultado) && esCadena(resultado)) {
						a.concatAssembler(prettyPrint(null, "MOV","si, OFFSET " + resultado, null));
						a.concatAssembler(prettyPrint(null, "MOV","di, OFFSET "+ raiz.izquierdo.elemento.toString(), null));
						a.concatAssembler(prettyPrint(null, "CALL COPIAR", null, null));
					}
					else{
						a.concatAssembler(prettyPrint(null, aux.getTipo().contains("FLOAT") ? "FLD" : "FILD", resultado , null));
						a.concatAssembler(prettyPrint(null, aux.getTipo().contains("FLOAT") ? "FSTP" : "FISTP", raiz.izquierdo.elemento.toString(), null));
						a.setResultado(raiz.izquierdo.elemento.toString());

						// Si es reservada devuelvo a cola
						returnReservada(resultado);
					}
				a.setTipo(tipo1);
				return a;
			}

			case ASIGNOMAS:{
				Assembler aux = generaAssembler(raiz.derecho);

				String tipo1 = Metodos.getTabla_simbolos().getTipo(raiz.izquierdo.elemento.toString());
				String tipo2 = aux.getTipo();
				String resultado = aux.getResultado();

				if(!tipo1.endsWith(tipo2) && !tipo2.endsWith(tipo1)){
							System.out.println("No se puede asignar " + tipo2 + " a " + tipo1);
							System.exit(1);
					}

				a.setAssembler(aux.getAssembler());
				a.concatAssembler(prettyPrint(null, aux.getTipo().contains("FLOAT") ? "FLD" : "FILD", resultado, null));
				a.concatAssembler(prettyPrint(null, aux.getTipo().contains("FLOAT") ? "FSTP" : "FISTP", raiz.izquierdo.elemento.toString(), null));
				a.setResultado(raiz.izquierdo.elemento.toString());
				a.setTipo(tipo1);

				// Si es reservada devuelvo a cola
				returnReservada(resultado);

				return a;
			}

			case CONDICION: {
				if(raiz.izquierdo.elemento.toString().equals("AND")) {

					ArrayList<String> tipos = new ArrayList<String>();
					tipos.add(generaAssembler(raiz.izquierdo.izquierdo.izquierdo).getTipo());
					tipos.add(generaAssembler(raiz.izquierdo.izquierdo.derecho).getTipo());
					tipos.add(generaAssembler(raiz.izquierdo.derecho.izquierdo).getTipo());
					tipos.add(generaAssembler(raiz.izquierdo.derecho.derecho).getTipo());

					if(sonCadenas(tipos)) {
						System.out.println("No se pueden comparar cadenas");
						System.exit(1);
					}

					String primerSalto = calcularJump(raiz.izquierdo.izquierdo);
					String etiqueta = extraerEtiqueta(primerSalto);
					String finIF = new String();

					if(raiz.derecho.derecho != null)
						finIF = "fin_complex" + etiqueta;

					if(raiz.derecho.izquierdo != null)
						finIF = "fin_complex" + etiqueta;
					
					a.concatAssembler(primerSalto); // JMP falso etiqueta
					String segundoSalto = calcularJump(raiz.izquierdo.derecho);
					a.concatAssembler(reemplazarEtiqueta(segundoSalto, etiqueta)); // JMP falso SEGUNDAETIQUETA
					
					// Puede ser nulo si es un OR y no tiene ELSE (OR es convertido a un AND)
					if(raiz.derecho.izquierdo != null)
						a.concatAssembler(generaAssembler(raiz.derecho.izquierdo).getAssembler()); // VERDADERO					

					// Puede ser nulo si no tiene ELSE
					if(raiz.derecho.derecho != null) {
						a.concatAssembler(prettyPrint(null, "JMP", finIF, null)); // JMP finIF
						a.concatAssembler(prettyPrint(etiqueta + ":", null, null, null)); // etiqueta
						a.concatAssembler(generaAssembler(raiz.derecho.derecho).getAssembler()); // falso
						a.concatAssembler(prettyPrint(finIF + ":", null, null, null));
					}
					else
						a.concatAssembler(prettyPrint(etiqueta + ":", null, null, null)); // etiqueta

					return a;
				}
				else{
					if(raiz.izquierdo.elemento.toString().equals("OR")) 
					{
						ArrayList<String> tipos = new ArrayList<String>();
						tipos.add(generaAssembler(raiz.izquierdo.izquierdo.izquierdo).getTipo());
						tipos.add(generaAssembler(raiz.izquierdo.izquierdo.derecho).getTipo());
						tipos.add(generaAssembler(raiz.izquierdo.derecho.izquierdo).getTipo());
						tipos.add(generaAssembler(raiz.izquierdo.derecho.derecho).getTipo());

						if(sonCadenas(tipos)) {
							System.out.println("No se pueden comparar cadenas");
							System.exit(1);
						}

						// Convierto subarbol a AND: a OR b == NOT [NOT a AND NOT b]
						raiz.izquierdo.elemento = "AND";
						
						// Invierto las operaciones de comparacion						
						raiz.izquierdo.izquierdo.elemento = invertir_comparacion(raiz.izquierdo.izquierdo.elemento.toString());
						raiz.izquierdo.derecho.elemento = invertir_comparacion(raiz.izquierdo.derecho.elemento.toString());
										
						// Hago el reemplazo de los resultados (el NOT exterior)
						Nodo aux = new Nodo();
						aux.derecho = raiz.derecho.izquierdo;
						aux.izquierdo = raiz.derecho.derecho;
						raiz.derecho=null;
						raiz.derecho=aux;
						
						// Genero assembler de una condicion doble AND
						a.concatAssembler(generaAssembler(raiz).getAssembler());

						return a;
					}

					else 				// No es condicion doble OR o AND
					{
						ArrayList<String> tipos = new ArrayList<String>();

						if(raiz.izquierdo.elemento.toString().equalsIgnoreCase("NOT")) {
							tipos.add(generaAssembler(raiz.izquierdo.izquierdo.izquierdo).getTipo());
							tipos.add(generaAssembler(raiz.izquierdo.izquierdo.derecho).getTipo());

							if(sonCadenas(tipos)) {
								System.out.println("No se pueden comparar cadenas");
								System.exit(1);
							}
						} else
						{
							tipos.add(generaAssembler(raiz.izquierdo.izquierdo).getTipo());
							tipos.add(generaAssembler(raiz.izquierdo.derecho).getTipo());

							if(sonCadenas(tipos)) {
								System.out.println("No se pueden comparar cadenas");
								System.exit(1);
						}
						}

						// raiz -> izquierdo = condicion del if
						String salto = calcularJump(raiz.izquierdo); 
						
						// Saco los comentarios, separo por espacios y tomo el tercero que es la etiqueta
						String falso = extraerEtiqueta(salto);
						
						// Escribo el salto
						a.concatAssembler(salto);

						// derecha.izquierdo -> verdadero, escribo las sentencias por verdadero
						a.concatAssembler(generaAssembler(raiz.derecho.izquierdo).getAssembler());
						
						// Si tiene else, pongo el salto incondicional al fin del if, para que no haga el falso despues de haber hecho el verdadero
						String finIF = new String();
						if(raiz.derecho.derecho != null) {
							finIF = "fin_if_" + falso;
							a.concatAssembler(prettyPrint(null, "JMP", finIF, null));
						}
						
						// derecha.derecho -> falso, pongo la etiqueta del falso, siempre
						a.concatAssembler(prettyPrint(falso + ":", null, null, null));
						
						// Si tiene else, escribo las sentencias del else, y pongo la etiqueta del salto incondicional
						if(raiz.derecho.derecho != null) {
							a.concatAssembler(generaAssembler(raiz.derecho.derecho).getAssembler());
							a.concatAssembler(prettyPrint(finIF + ":", null, null, null));
						}

						return a;
					}
				}
			}

			case OPSUMA: 
			case OPRESTA:
			case OPMULT:
			case OPDIV:{
			 	Assembler aux  = generaAssembler(raiz.izquierdo);
			 	Assembler aux2 = generaAssembler(raiz.derecho);
			 	String tipo1 = aux.getTipo();
				String tipo2 = aux2.getTipo();
				boolean es_float;

				// Si alguna de las variables es una cadena
				if(tipo1.endsWith("STRING") || tipo2.endsWith("STRING")){
					System.out.println("No se puede operar " + tipo2 + " a " + tipo1);
					System.exit(1);
				}

				// Si las variables son de distinto tipo
				if(!operables(tipo1, tipo2)){
							System.out.println("No se puede operar " + tipo2 + " a " + tipo1);
							System.exit(1);
					}

			 	a.concatAssembler(aux.getAssembler());
			 	a.concatAssembler(aux2.getAssembler());
			 	a.concatAssembler(cargarOperandos(aux, aux2));
			 	String resultado = pedirAux();
			 	switch(operacion){
			 		case OPSUMA:	a.concatAssembler(prettyPrint(null, "FADD", null, null)); break;
					case OPRESTA:	a.concatAssembler(prettyPrint(null, "FSUB", null, null)); break;
					case OPMULT:	a.concatAssembler(prettyPrint(null, "FMUL", null, null)); break;
					case OPDIV:		a.concatAssembler(prettyPrint(null, "FDIV", null, null)); break;
			 	}

			 	if(aux.getTipo().contains("FLOAT"))
			 		es_float = true;
			 	else
			 		es_float = false;

			 	a.concatAssembler(prettyPrint(null, es_float ? "FSTP" : "FISTP", resultado, null));
			 	a.concatAssembler(prettyPrint(null, "FFREE", "ST(0)", null));
			 	a.setResultado(resultado);
			 	a.setTipo(tipo1);
			 	return a;

			}

			case FOR: {
				Assembler asigParte1 = generaAssembler(raiz.izquierdo.izquierdo.izquierdo);
				String condParte1 = resolverOperadoresLogicos(raiz.izquierdo.izquierdo.derecho);
				String salto = "loop" + (indexFor - 1);		// no cambiar de lugar estas lineas, la recursividad
				String finFor = "finFor" + (indexFor - 1) + ":";	// puede afectar el valor de indexFor
				Assembler sentencias = generaAssembler(raiz.derecho);
				Assembler asigFinal = generaAssembler(raiz.izquierdo.derecho);

				a.concatAssembler(asigParte1.getAssembler());
				a.concatAssembler(prettyPrint(salto + ":", null, null, null));
				a.concatAssembler(condParte1);
				a.concatAssembler(sentencias.getAssembler());
				a.concatAssembler(asigFinal.getAssembler());
				a.concatAssembler(prettyPrint(null, "JMP", salto, null));
				a.concatAssembler(prettyPrint(finFor, null, null, null));

				return a;
			}

			case WPRINT: {
				// Me traigo lo que tengo que imprimir
				Assembler aux = generaAssembler(raiz.izquierdo);
				
				// Hago el assembler dependiendo de lo que tengo que imprimir
				if(esCadena(aux.getResultado())) {
					a.concatAssembler(prettyPrint(null, "MOV", "DX, OFFSET " + aux.getResultado(), null));
					a.concatAssembler(prettyPrint(null, "MOV", "Ah,9", null));
					a.concatAssembler(prettyPrint(null, "INT", "21h", null));
				} else {
					Token t = Metodos.getTabla_simbolos().getToken(aux.getResultado());
					if(t.getTipo().equals("cteINT") || t.getTipo().equals("INT"))
							a.concatAssembler(prettyPrint(null, "DisplayInteger", aux.getResultado(), null));
					else {
						if(t.getTipo().equals("FLOAT") || t.getTipo().equals("cteFLOAT"))
							a.concatAssembler(prettyPrint(null, "DisplayFloat", aux.getResultado()+", 2", null));
					}
				}
				
				// Nueva linea
				a.concatAssembler(prettyPrint(null, "CALL LINE", null, null));

				return a;
			}

			default: break;
		}

	// soy hoja? devuelvo mi dato
	if(raiz.izquierdo == null && raiz.derecho == null) {
		a.setResultado(raiz.elemento.toString());
		if(Metodos.getTabla_simbolos().contiene(raiz.elemento.toString()))
			a.setTipo(Metodos.getTabla_simbolos().getTipo(raiz.elemento.toString()));
		else
			a.setTipo("INT");
		return a;
	}

	// si llego aca, no hay que operar de ninguna forma particular y se recorre el arbol normalmente
	// esto es lo que escribe las sentencias de forma ordenada
	if(raiz.izquierdo != null)
		a.concatAssembler(generaAssembler(raiz.izquierdo).getAssembler());

	// por ser inorden,el padre aca deberia insertar su codigo, pero no tiene assembler ni resultados
	// es el caso de declarecur, sentencias, etc que aportaron recursividad y eran nodos necesarios para
	// conectar el arbol pero que no aportan

	if(raiz.derecho != null){
		Assembler aux5 = generaAssembler(raiz.derecho);
		if(raiz.elemento.toString().equals("sentencias")){
			a.setResultado(aux5.getResultado());
			a.setTipo("INT");
		}
		a.concatAssembler(aux5.getAssembler());
	}
	
	return a;
}

public String resolverOperadoresLogicos(Nodo raiz) {
	String ret = new String();
	if(raiz.elemento.toString().equals("AND")) {
		String fin = "finFor" + (indexFor++);
		ret += reemplazarEtiqueta(calcularJump(raiz.izquierdo), fin);
		ret += reemplazarEtiqueta(calcularJump(raiz.derecho), fin);
		return ret;
	}
	if(raiz.elemento.toString().equals("OR")) {
		String fin = "finFor" + (indexFor++);
		ret += reemplazarEtiqueta(invertir(calcularJump(raiz.izquierdo)), "orOk" + indexFor);
		ret += reemplazarEtiqueta(calcularJump(raiz.derecho), fin);
		ret += prettyPrint("orOk" + indexFor + ":", null, null, null);
		return ret;
	}
	return reemplazarEtiqueta(calcularJump(raiz), "finFor" + (indexFor++));
}

public String cargarOperandos(Assembler in1, Assembler in2) {
	String retorno = new String();
	String op1 = in1.getResultado();
	String op2 = in2.getResultado();
	boolean es_float;

	// Dependiendo el tipo pongo FLD o FILD
	if(in1.getTipo().contains("INT"))
		es_float = false;
	else
		es_float = true;

	retorno += prettyPrint(null, es_float ? "FLD" : "FILD", op1 , null);
	retorno += prettyPrint(null, es_float ? "FLD" : "FILD", op2 , null);

	// Si son reservadas retorno
	returnReservada(op1);
	returnReservada(op2);

	return retorno;
}

public boolean esCadena(String eval) {
	return Metodos.getTabla_simbolos().getTipo(eval).equalsIgnoreCase("cteString") || Metodos.getTabla_simbolos().getTipo(eval).equalsIgnoreCase("String");
}

public boolean sonCadenas(ArrayList<String> lista) {
	for(String cadena : lista) {
		if(!(cadena.equalsIgnoreCase("cteString") || cadena.equalsIgnoreCase("string")))
			return false;
	}
	return true;
}

// Metodo para tabular la salida del assembler
public String prettyPrint(String etiqueta, String operador, String operando, String comentario) {
	int WIDTH = 31;
	if(etiqueta == null ||etiqueta.isEmpty()) {
		for(int i = 0; i < WIDTH; i++) {
			etiqueta = new String();
			etiqueta += " ";
		}
	}
	if(operador == null ||etiqueta.isEmpty()) {
		for(int i = 0; i < 8; i++) {
			operador = new String();
			operador += " ";
		}
	}
	if(operando == null ||etiqueta.isEmpty()) {
		for(int i = 0; i < WIDTH; i++) {
			operando = new String();
			operando += " ";
		}
	}
	if(comentario == null ||etiqueta.isEmpty()) {
		comentario = "";
	}
	String retorno = "";
	retorno = String.format("%-" + WIDTH + "s %-" + 16 + "s %-" + WIDTH + "s ; %s\n", etiqueta, operador, operando, comentario);
	return retorno;
}

public void writeToFile(String assembler) {
	try {
		String PATH = "./final.asm";
		File f = new File(PATH);
		FileWriter fw = new FileWriter(f);
		PrintWriter pw = new PrintWriter(fw);
		pw.println(".MODEL SMALL");
		pw.println(".386");
		pw.println(".STACK 200h");
		pw.println("include number.asm");
		pw.println("include macros2.asm");
		pw.println("extrn STRLEN:proc, COPIAR:proc, CONCAT:proc, LINE:proc");
		pw.println();
		pw.println(".DATA\n");

		assembler = recorrerTabla() + insertarAuxiliares() + insertarVarAuxiliares() + assembler;
		assembler = insertarCode(assembler);
		pw.println(assembler);

		pw.println(prettyPrint(null, "MOV", "AX,4C00h", "Termina la ejecucion"));
		pw.println(prettyPrint(null, "INT", "21h", null));
		pw.println(prettyPrint("END", "MAIN", null, null));
		pw.close();
		fw.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

public String recorrerTabla() {
	ArrayList<Token> tabla = Tabla.simbolos;
	String declaracion = "";

	// Declaracion assembler de variables residentes en tabla de simbolos con tipo distinto de null
	for(Token t : tabla) {
		if(t.getTipo() != null && (t.getTipo().equals("cteFLOAT") || t.getTipo().equals("cteINT")))
			declaracion += prettyPrint(t.getNombre(), "dd", t.getValor().toString(), null);
		if(t.getTipo() != null && t.getTipo().equals("cteSTRING"))
			declaracion += prettyPrint(t.getNombre(), "db", "\""+ t.getValor()+ "\", '$', " + t.getLongitud() + " dup(?)", null);
		if(t.getTipo() != null && t.getTipo().equals("STRING"))
			declaracion += prettyPrint(t.getNombre(), "db", "31 dup(?)", null);
		if(t.getTipo() != null && t.getTipo().equals("INT"))
			declaracion += prettyPrint(t.getNombre(), "dd", "?", null);
		if(t.getTipo() != null && t.getTipo().equals("FLOAT"))
			declaracion += prettyPrint(t.getNombre(), "dd", "?", null);
	}

	// Variables privadas
	declaracion += prettyPrint("__concat__", "db", "31 dup(?)", "var reservada concatenacion");
	declaracion += prettyPrint("__endl__", "db", "0Dh, 0Ah, '$'", "var reservada fin linea");
	declaracion += prettyPrint("__1__", "dd", "1", "var reservada incremento FILTERC");
	declaracion += prettyPrint("__0__", "dd", "0", "var reservada reseteo FILTERC");
	return declaracion;
}

public String insertarAuxiliares() {
	String auxiliares = new String();
	for(int i = 1; i < indexAritmetico; i++)
		auxiliares += prettyPrint("__aux" + i + "__", "dd", "?", "var reservada");

	// Bloque assembler con declaracion de auxiliares
	return auxiliares;
}

public String insertarVarAuxiliares() {
	String varauxiliares = new String();
	for(int i = 1; i < indexVar; i++)
		varauxiliares += prettyPrint("__var" + i + "__", "dd", "?", "var reservada FILTERC");
	
	// Bloque assembler con declaracion de auxiliares FILTERC
	return varauxiliares;
}

public String insertarCode(String assembler) 
{
	int x = assembler.lastIndexOf("dd");
	int y = assembler.lastIndexOf("db");
	int z = assembler.lastIndexOf("dq");
	int mayor = x > y && x > z ? x : (y > z && y > x ? y : z);
	int split = assembler.indexOf("\n", mayor);
	String inicio = assembler.substring(0, split);
	String fin = assembler.substring(split, assembler.length());
	String a = prettyPrint(null, "MOV", "AX,@DATA", "inicializar datos");
	String b = prettyPrint(null, "MOV", "DS,AX", null);
	String c = prettyPrint(null, "MOV", "ES,AX", null);
	
	// Armado de bloque encabezado assembler
	return inicio + "\n\n.CODE\nMAIN:\n" + a + b + c + fin;
}

public String calcularJump(Nodo raiz) {
	String operacion = raiz.elemento.toString();
	String aux = new String();
	
	if(operacion.equals("NOT"))
		return invertir(calcularJump(raiz.izquierdo));
	
	else { // Una operacion de comparacion simple
		Assembler ladoIzq = generaAssembler(raiz.izquierdo);
		Assembler ladoDer = generaAssembler(raiz.derecho);
		aux += ladoIzq.getAssembler();
		aux += ladoDer.getAssembler();

		// Cargo operandos primero
		aux += cargarOperandos(ladoIzq,ladoDer);

		// Comparo
		aux += prettyPrint(null, "FXCH", null , null);
		aux += prettyPrint(null, "FCOMP", null, null);
		aux += prettyPrint(null, "FSTSW", "AX", null);
		aux += prettyPrint(null, "FFREE", "ST(0)", null);
		aux += prettyPrint(null, "SAHF", null, null);

		// Colocado de etiqueta para salto
		if(operacion.equals("MAYOR")){
			return aux + prettyPrint(null, "JBE", "menor_igual" + (indexSalto++), null);
		}
		if(operacion.equals("MENOR")) {
			return aux + prettyPrint(null, "JAE", "mayor_igual" + (indexSalto++), null);
		}
		if(operacion.equals("MAYORIGUAL")) {
			return aux + prettyPrint(null, "JB", "menor" + (indexSalto++), null);
		}
		if(operacion.equals("MENORIGUAL")) {
			return aux + prettyPrint(null, "JA", "mayor" + (indexSalto++), null);
		}
		if(operacion.equals("DISTINTO")) {
			return aux + prettyPrint(null, "JE", "igual" + (indexSalto++), null);
		}
		if(operacion.equals("IGUALIGUAL")) {
			return aux + prettyPrint(null, "JNE", "distinto" + (indexSalto++), null);
		}
	}
	return ""; // unreachable code
}

public String invertir(String linea) {
	if(linea.contains("JBE")) {
		return linea.replace("JBE", "JA");
	}
	if(linea.contains("JAE")) {
		return linea.replace("JAE", "JB");
	}
	if(linea.contains("JB")) {
		return linea.replace("JB", "JAE");
	}
	if(linea.contains("JA")) {
		return linea.replace("JA", "JBE");
	}
	if(linea.contains("JE")) {
		return linea.replace("JE", "JNE");
	}
	if(linea.contains("JNE")) {
		return linea.replace("JNE", "JE");
	}
	return "";
}

@SuppressWarnings("incomplete-switch")
public String invertir_comparacion(String input_st)
{
	String output_st = new String();
	Operaciones tipo_operacion = enumerator(input_st);

	switch(tipo_operacion)
	{
		case MAYOR:			output_st = "MENORIGUAL"; 	break;
		case MENOR:			output_st = "MAYORIGUAL";	break;
		case MAYORIGUAL:	output_st = "MENOR";		break;
		case MENORIGUAL:	output_st = "MAYOR";		break;
		case IGUALIGUAL:	output_st = "DISTINTO";		break;
		case DISTINTO:		output_st = "IGUALIGUAL";	break;
	}

	return output_st;
}

public String pedirAux() {
	// Cola de auxiliares
	if(stackAux.isEmpty())
		stackAux.push("__aux" + (indexAritmetico++) + "__");
	return stackAux.pop();
}

public String pedirVar() {
	// Cola de auxiliares FILTERC
	if(stackVar.isEmpty())
		stackVar.push("__var" + (indexVar++) + "__");
	return stackVar.pop();
}

public void returnReservada(String in){
	// Si es reservada devuelvo a cola
	if(in.startsWith("__aux"))
		stackAux.push(in);
	else
		if(in.startsWith("__var"))
			stackVar.push(in);
}

public String extraerEtiqueta(String string) {
	String[] lineas = string.split("\n");
	String[] vector = null;
	String resultado = null;
	for(String a : lineas) {
		vector = a.replaceAll(";+.*", "").split(" +");
		for(int i = 0; i < vector.length; i++) {
			if(vector[i].startsWith("J")) {
				resultado = vector[i+1];
			}
		}
	}
	return resultado;
}

public String reemplazarEtiqueta(String string, String reemp) {
	String[] lineas = string.split("\n");
	Pattern p = Pattern.compile(";+.*");
	Matcher m = p.matcher(lineas[lineas.length-1]);
	m.find();
	String comment = lineas[lineas.length-1].substring(m.start());
	String[] vector = lineas[lineas.length-1].replaceAll(";+.*", "").split(" +");
	lineas[lineas.length-1] = prettyPrint(vector[0], vector[1], reemp, comment.replaceAll("; ", ""));
	String retorno = new String();
	for(String linea : lineas) {
		retorno += linea +'\n';
	}
	return retorno;
}

public void dbug(String s) {
	if(debuggear)
		System.out.println(s);
}

public boolean operables(String a, String b) {
	String tipo1 = a.toUpperCase();
	String tipo2 = b.toUpperCase();

	if(tipo1.contains("INT"))
		if(tipo2.contains("INT"))
			return true;

	if(tipo1.contains("FLOAT"))
		if(tipo2.contains("FLOAT"))
			return true;

	return false;
}